/*
*	Title: tcptoken.c
*	
*	Code that implements the connection library to be used 
*	on our assignment. This code abstracts a TCP
*	connection and how to set on up.
*
*	Date: 22/09/2013
*	Autors: Danilo Barbosa Porto , Henrique Alberto Rusa
*/

#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>
#include"tcptoken.h"

/*
*	Function to initiate and instantiate connection variables
*/
void newServer(servtoken *Server, int portno) {
	
	// Allocating memory within the type defined
	*Server = (servtoken)malloc(sizeof(struct serv));
	if(*Server == NULL) {
		fprintf(stderr,"ERROR allocating memory to server: %s \n",strerror(errno));
		exit(0);
	}
	(*Server)->portNumber = portno;
	
	/*
	*	Creating a socket where the server will accept connections
	*	AF_INET determines internet address domain, SOCK_STREAM put a stream socket
	*	and the 0 is for protocol TCP
	*/
	(*Server)->socket = socket(AF_INET, SOCK_STREAM, 0);
	
	// Setting socket options
	int optval = 1;
	if((setsockopt((*Server)->socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))) == -1 ) {
		fprintf(stderr,"ERROR setting socket options: %s \n", strerror(errno));
		exit(0);
	}

	bzero( (char*)&(*Server)->server_address, sizeof((*Server)->server_address));
	
	(*Server)->server_address.sin_family = AF_INET;
	(*Server)->server_address.sin_port = htons((*Server)->portNumber); // htons convert the port number to network byte order
	(*Server)->server_address.sin_addr.s_addr = htonl(INADDR_ANY); // the server IP if the program works as a server. INADDR_ANY gets the IP automatically
	
	// bind an address to a socket
	if(bind((*Server)->socket, (struct sockaddr *)&(*Server)->server_address, sizeof((*Server)->server_address)) < 0) {
		fprintf(stderr,"ERROR binding address to socket: %s \n",strerror(errno));
		exit(0);
	}
	
	// listening to a connection, with a queue at max QueueSize requisitions
	listen((*Server)->socket, QUEUESIZE);
}

void serverStart(servtoken *Server) {
	
	/* 
	*	accept() blocks until a connection is made.
	*	When a connection is established, NewSocketfileDescriptor is the reference to this
	*	connection. 
	*/
	(*Server)->clientLenght = sizeof((*Server)->client_address);
	(*Server)->newSocket = accept((*Server)->socket, (struct sock_addr *)&(*Server)->client_address, &(*Server)->clientLenght);
	if((*Server)->newSocket <= 0) {
		fprintf(stderr,"ERROR accepting connection: %s \n",strerror(errno));
	}
}

int serverRead(servtoken *Server, void *buffer, int ByteSize) {
	
	int n = 0;
	
	/*
	*	Empty the buffer with bzero().
	*	read n bytes (or characters) from the connection, or 255, whichever is less. 
	*/
		
	n = recv((*Server)->newSocket, (char *)buffer, ByteSize, MSG_WAITALL);
	if(n < 0) {
		fprintf(stderr,"ERROR reading from socket: %s \n", strerror(errno));
		exit(0);
	}
		
	return n;
}

int serverWrite(servtoken *Server, void *buffer, int ByteSize) {
	
	int n = 0;
	
	/*
	*	Writes bytes to the connection
	*/
	n = send((*Server)->newSocket, (char *)buffer, ByteSize, MSG_EOR);
	if(n < 0) {
		fprintf(stderr,"ERROR writing to socket: %s \n",strerror(errno));
		exit(0);
	}
		
	return n;
}

int closeServer(servtoken *Server) {
	
	/*
	*	Function that shuts down the connection on the server side. 
	*/
	int n = 0;
	n = shutdown((*Server)->socket, 2);
	Server = NULL;
 
	return n;
}

/*
*	Function to initiate and instantiate connection variables
*/
void newClient(clientoken *Client, int portno, char *hostname) {

	// Allocating memory within the type defined
	*Client = (clientoken)malloc(sizeof(struct client));
	if(*Client == NULL) {
		fprintf(stderr,"ERROR allocating memory to client: %s \n",strerror(errno));
		exit(0);
	}
	
	// Third argument is the port that the program will be listening
	(*Client)->portNumber = portno;
	
	(*Client)->socket = socket(AF_INET, SOCK_STREAM, 0);
	if((*Client)->socket < 0) {
		fprintf(stderr,"ERROR opening socket: %s \n",strerror(errno));
		exit(0);
	}
		
	// first argument is the host name 
	(*Client)->server = gethostbyname(hostname);
	if((*Client)->server == NULL) {
		fprintf(stderr,"ERROR no such host: %s \n",strerror(errno));
		exit(0);
	}
		
	// initiates the server address with zeros.
	bzero((char *)&((*Client)->server_address), sizeof((*Client)->server_address));
	
	// Setting Server_Address fields, and bcopy to transfer a char string to a byte order to network string
	(*Client)->server_address.sin_family = AF_INET;
	bcopy((char *)(*Client)->server->h_addr, (char *)&(*Client)->server_address.sin_addr.s_addr, (*Client)->server->h_length);
	(*Client)->server_address.sin_port = htons((*Client)->portNumber);
} 

void clientStart(clientoken *Client) {
	
	// connect the client to the address and port specified for this socket.
	if(connect((*Client)->socket, (struct sockaddr *)&(*Client)->server_address, sizeof((*Client)->server_address)) < 0) {
		fprintf(stderr, "ERROR cannot connect: %s \n",strerror(errno));
		exit(0);
	}
}

int clientRead(clientoken *Client, void *buffer, int ByteSize) {
	
	// Reads ByteSize bytes from the connection
	int n = recv((*Client)->socket,(char *)buffer, ByteSize, MSG_WAITALL);
	if (n < 0) {
		fprintf(stderr,"ERROR reading from socket: :%s \n",strerror(errno));
		exit(0);
	}
		
	return n;
}

int clientWrite(clientoken *Client, void *buffer, int ByteSize) {
	
	int n;
	
	// send ByteSize bytes to the connection
	n = send((*Client)->socket,(char *)buffer, ByteSize, MSG_EOR);
	if (n < 0) {
		fprintf(stderr,"ERROR writing to socket: %s \n",strerror(errno));
		exit(0);
	}
		
	return n;
}

int closeClient(clientoken *Client) {

	
	// Function that shuts down the connection on the client side. 
	int n = 0;
	n = shutdown((*Client)->socket, 2);
	Client = NULL;
	
	return n;
}















